import { Badge, CreateBadgeDto, User } from '@boticord/users-client'
import { Body, ConflictException, Controller, Delete, Get, NotFoundException, Param, Post } from '@nestjs/common'
import { InjectRepository } from '@nestjs/typeorm'
import { Repository } from 'typeorm'
import { Action, PermissionsTo } from '@boticord/auth'
import { ErrorCodes } from '@boticord/consts'

@Controller('badges')
export class BadgeController {
  constructor(
    @InjectRepository(Badge) private readonly badges: Repository<Badge>,
    @InjectRepository(User) private readonly users: Repository<User>,
  ) {}

  /** gets all badges */
  @Get()
  async getBadges() {
    return this.badges.find()
  }

  /** creates a new badge */
  @Post()
  @PermissionsTo(Action.Create, Badge)
  async createBadge(@Body() dto: CreateBadgeDto) {
    const badge = new Badge()
    badge.description = dto.description
    badge.assetURL = dto.assetURL
    badge.name = dto.name
    
    return await this.badges.save(badge)
  }

  /** grants user a badge */
  @Post('user/:userID/:badgeID')
  @PermissionsTo(Action.Grant, Badge)
  async grantBadge(@Param('userID') userID: string, @Param('badgeID') badgeID: string) {
    const [ user, badge ] = await Promise.all([
      this.users.findOne({ where: { id: userID }, relations: { badges: true } }),
      this.badges.findOne({ where: { id: badgeID } }),
    ])

    if (user === null) {
      throw new NotFoundException({ code: ErrorCodes.UnknownUser })
    }

    if (badge === null) {
      throw new NotFoundException({ code: ErrorCodes.UnknownBadge })
    }

    if (user.badges.some(e => e.id === badgeID)) {
      throw new ConflictException({ code: ErrorCodes.UserAlreadyHasABadge })
    }

    user.badges.push(badge)
    
    return await this.users.save(user)
  }

  /** revokes a badge from user */
  @Delete('user/:userID/:badgeID')
  @PermissionsTo(Action.Grant, Badge)
  async revokeBadge(@Param('userID') userID: string, @Param('badgeID') badgeID: string) {
    const [ user, badge ] = await Promise.all([
      this.users.findOne({ where: { id: userID }, relations: { badges: true } }),
      this.badges.findOne({ where: { id: badgeID } }),
    ])

    if (user === null) {
      throw new NotFoundException({ code: ErrorCodes.UnknownUser })
    }

    if (badge === null) {
      throw new NotFoundException({ code: ErrorCodes.UnknownBadge })
    }

    if (!user.badges.some(e => e.id === badgeID)) {
      throw new NotFoundException({ code: ErrorCodes.UnknownBadge })
    }

    // delete specified badge from user
    user.badges = user.badges.filter(e => e.id !== badgeID)

    return await this.users.save(user)
  }

  /** deletes a badge */
  @Delete(':id')
  @PermissionsTo(Action.Delete, Badge)
  async deleteBadge(@Param('id') badgeID: string) {
    // badge from profiles will automatically be removed by cascades
    const badge = await this.badges.findOne({ where: { id: badgeID } })
    if (badge === null) {
      throw new NotFoundException({ code: ErrorCodes.UnknownBadge })
    }

    await this.badges.delete(badge)
    return true
  }
}